#include "SettingsParser.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <fcntl.h>
#include <unistd.h>

// SettingsParser类的构造函数
// 负责初始化SettingsParser对象，并从指定的文件路径加载设置
// 参数:
//   filePath: 包含设置的文件路径
SettingsParser::SettingsParser(const std::string& filePath) : filePath(filePath) {
    // 在SettingsParser对象创建时，调用loadSettingsFromFile方法从文件中加载设置
    loadSettingsFromFile();
}

// 从文件中加载设置
void SettingsParser::loadSettingsFromFile() {
    // 尝试打开设置文件
    std::ifstream file(filePath);
    // 如果文件无法打开，则输出错误信息并返回
    if (!file.is_open()) {
        std::cerr << "Failed to open settings file: " << filePath << std::endl;
        return;
    }

    // 清空之前的设置，准备加载新的设置
    settingsMap.clear();
    orderList.clear();

    // 逐行读取文件内容
    std::string line;
    while (std::getline(file, line)) {
        // 移除行两端的空白字符
        line = trim(line);

        // 忽略空行或注释行
        if (line.empty() || line[0] == '#') continue;

        // 查找等号位置，用于分割key和value
        size_t equalPos = line.find('=');
        // 如果找到等号，则解析这一行
        if (equalPos != std::string::npos) {
            // 提取key并移除其两端的空白字符
            std::string key = trim(line.substr(0, equalPos));
            // 提取value并移除其两端的空白字符
            std::string value = trim(line.substr(equalPos + 1));

            // 如果是重复的 key，则不会重复加入 orderList
            if (settingsMap.find(key) == settingsMap.end()) {
                orderList.push_back(key);
            }
            // 将解析出的键值对存入设置映射中
            settingsMap[key] = value;
        }
    }

    // 关闭文件
    file.close();
}

/**
 * @brief 获取指定键的设置值
 * 
 * 该函数通过给定的键名在设置映射中查找对应的值如果找到了对应的键值对，
 * 则返回该键对应的值；如果未找到，则返回空字符串此函数常用于需要查询
 * 某个设置项值的场景，通过键名快速获取值
 * 
 * @param key 设置项的键名
 * @return std::string 对应键名的设置值，如果键不存在则返回空字符串
 */
std::string SettingsParser::getSetting(const std::string& key) const {
    // 尝试在设置映射中找到与键名对应的迭代器
    auto it = settingsMap.find(key);
    // 如果找到了对应的键值对，则返回该键对应的值
    if (it != settingsMap.end()) {
        return it->second;
    }
    // 如果没有找到对应的键值对，则返回空字符串
    return "";
}
/**
 * 将指定的设置项解析为整数。
 * 
 * 此函数首先使用 `getSetting(key)` 方法获取与指定键关联的设置值字符串。
 * 然后，它尝试将该字符串转换为整数。如果转换成功，则返回转换后的整数值。
 * 如果设置值字符串为空或转换失败，则返回 -1。
 * 
 * @param key 设置项的键。
 * @return 转换为整数的设置值，如果转换失败则为 -1。
 */
int SettingsParser::getSettingAsInt(const std::string& key) const {
    // 获取与键关联的设置值字符串
    std::string valueStr = getSetting(key);
    
    // 确保设置值字符串不为空
    if (!valueStr.empty()) {
        try {
            // 尝试将设置值字符串转换为整数
            return std::stoi(valueStr);
        } catch (...) {
            // 转换失败
        }
    }
    // 如果设置值字符串为空或转换失败，返回 -1
    return -1;
}

/**
 * 设置或更新一个配置项
 * 
 * @param key 配置项的键如果键不存在于配置中，它会被添加
 * @param value 配置项的值
 * @return 总是返回 true，表示操作成功
 * 
 * 本函数首先尝试在 orderList 中查找给定的 key 如果 key 不存在，它会被添加到 orderList 的末尾
 * 这样做是为了维护配置项的插入顺序随后，无论 key 是否已存在，都会在 settingsMap 中更新或插入对应的 value
 * 这确保了配置项的值被正确设置
 */
bool SettingsParser::setSetting(const std::string& key, const std::string& value) {
    // 尝试在顺序列表中查找 key
    auto it = std::find(orderList.begin(), orderList.end(), key);
    // 如果 key 不存在于顺序列表中
    if (it == orderList.end()) {
        orderList.push_back(key); // 新增的 key，添加到顺序列表末尾
    }
    // 在配置映射中更新或插入 key-value 对
    settingsMap[key] = value;
    // 操作成功，返回 true
    return true;
}

/**
 * 将整数类型的设置值更新到配置中
 * 
 * 该函数接受一个设置项的键和一个整数值，将该整数值转换为字符串后，
 * 调用setSetting函数来更新配置项这种转换是必要的，因为配置项的值在内部
 * 通常以字符串形式存储
 * 
 * @param key 设置项的键，用于唯一标识一个设置项
 * @param value 要设置的整数值
 * @return 更新操作是否成功
 */
bool SettingsParser::setSettingAsInt(const std::string& key, int value) {
    return setSetting(key, std::to_string(value));
}

/**
 * 设置参数解析器的配置信息
 * 
 * 本函数通过接受一个无序映射（unordered_map），其中键为配置项的名称，值为配置项的值
 * 遍历这个映射，将每一对配置项设置到解析器中
 * 
 * @param newSettings 一个包含配置项名称和值的无序映射
 */
void SettingsParser::setSettings(const std::unordered_map<std::string, std::string>& newSettings) {
    // 遍历无序映射中的每一个键值对
    for (const auto& pair : newSettings) {
        // 调用setSetting函数，将配置项名称和值设置到解析器中
        setSetting(pair.first, pair.second);
    }
}

// 将设置保存到文件中
// 返回值: 保存成功返回true，否则返回false
bool SettingsParser::saveSettingsToFile() const {
    // 创建一个临时文件路径
    std::string tempFilePath = filePath + ".tmp";

    // 打开临时文件以写入设置
    std::ofstream file(tempFilePath);
    if (!file.is_open()) {
        // 如果文件无法打开，则输出错误信息并返回false
        std::cerr << "Failed to open temporary settings file for writing: " << tempFilePath << std::endl;
        return false;
    }

    // 遍历设置的有序列表，将每个设置写入临时文件
    for (const auto& key : orderList) {
        auto it = settingsMap.find(key);
        if (it != settingsMap.end()) {
            // 写入格式为“key=value”
            file << key << "=" << it->second << "\n";
        }
    }

    // 关闭临时文件
    file.close();

    // 使用 rename 确保原子性
    if (std::rename(tempFilePath.c_str(), filePath.c_str()) != 0) {
        // 如果重命名失败，则输出错误信息，删除临时文件，并返回false
        std::cerr << "Failed to rename temporary settings file." << std::endl;
        std::remove(tempFilePath.c_str());
        return false;
    }

    // 强制同步磁盘（fsync）
    int fd = open(filePath.c_str(), O_WRONLY);
    if (fd != -1) {
        // 如果成功打开文件描述符，则执行fsync并关闭文件描述符
        fsync(fd);
        close(fd);
    } else {
        // 如果无法打开文件描述符，则输出错误信息并返回false
        std::cerr << "Failed to open file descriptor for fsync." << std::endl;
        return false;
    }

    // 所有操作成功完成，返回true
    return true;
}

// 返回当前设置解析器中的所有设置
// 此函数允许外部以只读方式访问内部存储的所有设置
// 返回值: 包含所有设置的只读unordered_map，映射关系为设置名称到设置值
const std::unordered_map<std::string, std::string>& SettingsParser::getAllSettings() const {
    return settingsMap;
}

/**
 * 获取设置项的顺序列表
 * 
 * 此函数返回一个常量引用，指向存储设置项顺序的标准容器vector
 * 该列表用于维护设置项的顺序，以便在需要时按照特定的顺序处理这些设置项
 * 
 * @return const std::vector<std::string>& 一个常量引用，指向存储设置项顺序的vector
 */
const std::vector<std::string>& SettingsParser::getOrderList() const {
    return orderList;
}

// 打印所有设置项的函数
void SettingsParser::printAllSettings() const {
    // 遍历保存设置项顺序的列表
    for (const auto& key : orderList) {
        // 在设置项映射中查找当前键对应的值
        auto it = settingsMap.find(key);
        // 如果找到了对应的设置项，则打印其键值对
        if (it != settingsMap.end()) {
            std::cout << key << " = " << it->second << std::endl;
        }
    }
}

/**
 * @brief 去除字符串首尾的空白字符
 * 
 * 该函数主要用于去除给定字符串首尾的空白字符，包括空格和制表符。它首先查找字符串中第一个非空白字符的位置，
 * 如果未找到任何非空白字符，则返回一个空字符串。如果找到了非空白字符，则进一步查找最后一个非空白字符的位置，
 * 然后从第一个非空白字符到最后一个非空白字符之间截取子字符串，并返回该子字符串。
 * 
 * @param str 需要处理的原始字符串
 * @return std::string 去除首尾空白字符后的字符串
 */
std::string SettingsParser::trim(const std::string& str) const {
    // 查找第一个非空白字符的位置
    size_t first = str.find_first_not_of(" \t");
    // 如果未找到任何非空白字符，则返回空字符串
    if (first == std::string::npos)
        return "";

    // 查找最后一个非空白字符的位置
    size_t last = str.find_last_not_of(" \t");
    // 截取并返回从第一个非空白字符到最后一个非空白字符之间的子字符串
    return str.substr(first, (last - first + 1));
}